package cn.elead.chaos.framework.config;

import java.util.Collections;
import java.util.Map;
import java.util.stream.Collectors;

import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.SearchStrategy;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.boot.autoconfigure.web.servlet.error.ErrorViewResolver;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.error.ErrorAttributes;
import org.springframework.boot.web.servlet.error.ErrorController;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.env.Environment;
import org.springframework.web.context.request.RequestContextListener;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;

import cn.elead.chaos.api.web.IExceptionResolverService;
import cn.elead.chaos.core.util.ApplicationUtils;
import cn.elead.chaos.framework.properties.ChaosProperties;
import cn.elead.chaos.framework.web.endpoint.EpomEndPoint;
import cn.elead.chaos.framework.web.endpoint.EpomRequestMappingHandlerMapping;
import cn.elead.chaos.framework.web.enums.EnumEndpoint;
import cn.elead.chaos.framework.web.enums.EnumScan;
import cn.elead.chaos.framework.web.error.EpomErrorController;
import cn.elead.chaos.framework.web.global.exception.ChaosHandlerExceptionResolver;
import cn.elead.chaos.framework.web.global.exception.DefaultExceptionResolverService;
import cn.elead.chaos.framework.web.global.response.ApiControllerAdvice;
import lombok.extern.slf4j.Slf4j;

/**
 * 组件相关注册
 *
 * @author luopeng
 */
@Slf4j
@Configuration
@EnumScan
public class WebBeanConfiguration {

	/**
	 * spring上下文
	 * 
	 * @return
	 */
	@Bean
	@ConditionalOnMissingBean(ApplicationUtils.class)
	public ApplicationUtils applicationUtils() {
		return new ApplicationUtils();
	}

	/**
	 * 统一数据返回结构包装
	 * 
	 * @return
	 */
	@Bean
	@ConditionalOnMissingBean(ApiControllerAdvice.class)
	public ApiControllerAdvice apiControllerAdvice() {
		log.info("开启统一数据返回结构包装");
		return new ApiControllerAdvice();
	}

	/**
	 * 默认扩展异常
	 * 
	 * @return
	 */
	@Bean
	public DefaultExceptionResolverService epomImplHandlerExceptionResolver() {
		return new DefaultExceptionResolverService();
	}

	/**
	 * 全局异常
	 * 
	 * @return
	 */
	@Bean
	@ConditionalOnMissingBean(ChaosHandlerExceptionResolver.class)
	public ChaosHandlerExceptionResolver epomHandlerExceptionResolver(Map<String, IExceptionResolverService> resolverServices) {
		return new ChaosHandlerExceptionResolver(resolverServices);
	}

	/**
	 * 全局错误
	 * 
	 * @return
	 */
	@Bean
	@ConditionalOnMissingBean(value = ErrorController.class, search = SearchStrategy.CURRENT)
	public EpomErrorController epomErrorController(ErrorAttributes errorAttributes, ObjectProvider<ErrorViewResolver> errorViewResolvers, ServerProperties serverProperties) {
		return new EpomErrorController(errorAttributes, serverProperties.getError(), errorViewResolvers.orderedStream().collect(Collectors.toList()));
	}

	/**
	 * 跨域支持
	 * <p>
	 * https://cloud.tencent.com/developer/article/1472629
	 * 
	 * @param chaosProperties
	 * @return
	 */
	@Bean
	public FilterRegistrationBean<CorsFilter> corsFilter(ChaosProperties chaosProperties) {
		UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
		CorsConfiguration corsConfiguration = new CorsConfiguration();
		corsConfiguration.setAllowCredentials(true);
		corsConfiguration.setAllowedOrigins(Collections.singletonList(CorsConfiguration.ALL));
		corsConfiguration.setAllowedHeaders(Collections.singletonList(CorsConfiguration.ALL));
		corsConfiguration.setAllowedMethods(Collections.singletonList(CorsConfiguration.ALL));
		corsConfiguration.addExposedHeader(chaosProperties.getCross().getAccessControlAllowHeaders());
		source.registerCorsConfiguration(chaosProperties.getCross().getPathPatterns(), corsConfiguration);
		FilterRegistrationBean<CorsFilter> bean = new FilterRegistrationBean<CorsFilter>(new CorsFilter(source));
		bean.setOrder(Ordered.HIGHEST_PRECEDENCE);
		log.info("开启全局跨域支持");
		return bean;
	}

	/**
	 * request请求被spring管理
	 * 
	 * @return
	 */
	@Bean
	@ConditionalOnMissingBean(RequestContextListener.class)
	public RequestContextListener requestContextListener() {
		return new RequestContextListener();
	}

	/**
	 * 枚举信息端点配置
	 * 
	 * @param environment
	 * @return
	 */
	@Bean
	@ConditionalOnMissingBean(EnumEndpoint.class)
	public EnumEndpoint enumEndpoint(Environment environment) {
		return new EnumEndpoint(environment);
	}

	/**
	 * 枚举信息端点配置
	 * 
	 * @param environment
	 * @return
	 */
	@Bean
	@ConditionalOnMissingBean(EpomRequestMappingHandlerMapping.class)
	public EpomRequestMappingHandlerMapping epomRequestMappingHandlerMapping(Map<String, EpomEndPoint> epomEndPoints) {
		log.info("开启枚举信息端点");
		return new EpomRequestMappingHandlerMapping(epomEndPoints);
	}

}
